/**
 * 
 */
package primary_classes;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

import secondary_classes.Order;
import secondary_classes.OrderBook;
import secondary_classes.OrderBook.AmendType;
import secondary_classes.OrdersComparatorDateTime;
import secondary_classes.ReadOrders;
import secondary_classes.SortedList;
import _runnable_classes.TradingEngine;
import _runnable_classes.TradingEngine.PHASE;

/**
 * @author Rohit
 *
 */
public class OrderBookList extends HashMap<String, OrderBook> {

	/**
	 * 
	 */
	private static final long    serialVersionUID = 1L;
	private static final String  fields = "#Instrument,Date,Time,Record Type,Price,Volume,Undisclosed Volume,Value,Qualifiers,Trans ID,Bid ID,Ask ID,Bid/Ask,Entry Time,Old Price,Old Volume";
	
	/* (non-Javadoc)
	 * @see java.util.AbstractCollection#toString()
	 */
	@Override
	public String toString() {
		String result = "";

		for (OrderBook o : this.values()) {
			result += o.getName() + ":\n";
			result += o.toString();
			result += "\n\n";
		}
		return result;
	}

	public void enter(Order o) throws Exception {
		// check the phase and match accordingly
		String name = o.getInstrument();
		OrderBook orderBook;
		if (this.containsKey(name)) {
			orderBook = get(name);
		} else {
			orderBook = new OrderBook(name);
			put(name, orderBook);
		}

		orderBook.enter(o);


		PHASE phase = TradingEngine.getCurrentPhase();

		if (phase.equals(PHASE.normalTrading)) {
			orderBook.match(o.getDate(), o.getTime());
		}
	}

	public void delete(Order o) throws Exception {
		OrderBook orderBook = get(o.getInstrument());
		if (orderBook != null) {
			orderBook.delete(o);
			if (orderBook.numBids() == 0 && orderBook.numAsks() == 0 && orderBook.numTrades() == 0) {
				remove(orderBook);
			}
		}
	}

	public void amend(Order o) throws Exception {
		OrderBook toBeChangedBook = get(o.getInstrument());
		if (toBeChangedBook != null) {
			Order toBeChanged = toBeChangedBook.findOrder(o.getOrderId());
			if (toBeChanged != null) {
				AmendType amendType = toBeChanged.getAmendType(o);
				if (amendType != null) {
					String name = toBeChanged.getInstrument();
					OrderBook orderBook = get(name);
					orderBook.amend(amendType, toBeChanged, o);
				}
			}
		}
	}

	public void writeMarketDepthFile(int marketDepthSize, File outputFile) throws Exception {
		System.out.print(TradingEngine.elapsedTimeString() + "  MARKET DEPTH FILE" + "\r");
		String result = "";

		// create output file
		BufferedWriter b;
		try {
			b = new BufferedWriter(new FileWriter(outputFile));
		} catch (Exception e) {
			Exception x = new Exception("Could not write market depth file.");
			x.setStackTrace(e.getStackTrace());
			throw x;
		}

		// Print headings
		System.out.print(TradingEngine.elapsedTimeString() + "  MARKET DEPTH FILE - Headings" + "\r");
		result += ReadOrders.requiredFields[0] + "," + ReadOrders.requiredFields[1] + "," + ReadOrders.requiredFields[2] + "," + ReadOrders.requiredFields[3];
		for (int i = 0; i < marketDepthSize; i++) {
			result += ",L" + (i + 1) + "-Bid Price";
			result += ",L" + (i + 1) + "-Bid Volume";
			result += ",L" + (i + 1) + "-Number of Buyers";
			result += ",L" + (i + 1) + "-Ask Price";
			result += ",L" + (i + 1) + "-Ask Volume";
			result += ",L" + (i + 1) + "-Number of Sellers";
		}
		result += "\n";

		// compile market depth data
		int numValues = values().size();
		int i = 0;
		for (OrderBook o : values()) {
			System.out.print(TradingEngine.elapsedTimeString() + "  MARKET DEPTH FILE - Order book " + (i + 1) + " of " + numValues + "\r");
			result += o.getMarketDepthFileContents(marketDepthSize, outputFile) + "\n";
			i++;
		}

		// Write to file
		b.write(result);
		b.close();
		System.out.println(TradingEngine.elapsedTimeString() + "  MARKET DEPTH FILE - Done (Wrote " + numValues + " order book(s) to a depth of " + marketDepthSize + ")");
	}

	public void writeTradesFile(File outputFile) throws Exception {
		String result = "";

		// create output file
		System.out.print(TradingEngine.elapsedTimeString() + "  TRADES FILE - Creating output file" + "\r");
		BufferedWriter b;
		try {
			b = new BufferedWriter(new FileWriter(outputFile));
		} catch (Exception e) {
			Exception x = new Exception("Could not write orders file.");
			x.setStackTrace(e.getStackTrace());
			throw x;
		}

		// headings
		result += fields + "\n";

		// content
		SortedList allOrdersArray = new SortedList(new OrdersComparatorDateTime());
		allOrdersArray.addAll(TradingEngine.ordersData.getOrders());
		int j = 0;
		while (j < allOrdersArray.size()) {
			Order o = allOrdersArray.get(j);
			if (o.getRecordType().equals("TRADE")) {
				allOrdersArray.remove(j);
			} else {
				j++;
			}
		}
		int orderNum = 0;
		Collection<OrderBook> books = values();
		for (OrderBook ob : books) {
			ArrayList<Order> allTrades = ob.getTrades();
			for (Order o : allTrades) {
				System.out.print(TradingEngine.elapsedTimeString() + "  TRADES FILE - Compiling order " + (orderNum + 1) + "\r");
				allOrdersArray.add(o);
				orderNum++;
			}
		}

		// compile the list of sorted orders for writing
		b.write(result);
		int numOrders = allOrdersArray.size();
		Order o;
		for (int i = 0; i < numOrders; i++) {
			System.out.print(TradingEngine.elapsedTimeString() + "  TRADES FILE - Writing order " + (i + 1) + " of " + numOrders + "\r");
			o = allOrdersArray.get(i);
			b.append(o.toString() + "\n");
		}

		b.close();

		System.out.println(TradingEngine.elapsedTimeString() + "  TRADES FILE - Done (Wrote " + numOrders + " orders to file)");
	}

	public void opening(Order o) throws Exception {
		Collection<OrderBook> c = this.values();
		Iterator<OrderBook> itr = c.iterator();
		while (itr.hasNext()) {
			OrderBook book = itr.next();
			book.opening(o.getDate(), o.getTime());
		}

	}

	public void closing() {
		Collection<OrderBook> c = this.values();
		Iterator<OrderBook> itr = c.iterator();
		while (itr.hasNext()) {
			OrderBook book = itr.next();
			book.closing();
		}
	}

}
